<template>
  <div>
    <el-upload action="#" :limit="limit" :file-list="fileList" :list-type="listType" :show-file-list="showFileList"
      :http-request="handleUploadEvent" :accept="accept" :before-upload="handleBeforeUploadEvent"
      :on-exceed="handleExceedEvent" :on-remove="handleRemoveEvent" :on-change="handleChangeEvent" :auto-upload="true">
      <el-button type="primary" :disabled="disabled">{{ btnName }}</el-button>
    </el-upload>
    <div v-if="showTip" slot="tip" class="el-upload__tip">只能上传{{ accept }}文件，且不超过10MB</div>
  </div>
</template>

<script lang="ts" setup>
import COS from "cos-js-sdk-v5";
import http from "@/utils/httpRequest";
import { ref, watch, onMounted, reactive, computed } from "vue";
import { ElMessage } from "element-plus";

// 属性定义
const props = defineProps({
  modelValue: [String, Array],
  showTip: {
    type: Boolean,
    default() {
      return true
    }
  },
  showFileList: {
    type: Boolean,
    default() {
      return true
    }
  },
  listType: {
    type: String,
    default() {
      return 'picture-card'
    }
  },
  accept: {
    type: String,
    default() {
      return '.jpg,.png,.avif'
    }
  },
  btnName: {
    type: String,
    default() {
      return '点击上传'
    }
  },
  limit: Number
});

// 上传组件是否可用
const disabled = ref(true);

// 上传组件
const cos = ref(undefined);

// 上传路径
const path = ref('');

// 上传组件配置
const config = reactive({
  bucket: 'jiangyg-1307736031',
  region: 'ap-beijing',
  storageClass: 'STANDARD'
});

// 正在上传的文件
const file = ref(undefined);

// 父级组件的自定义事件
const emit = defineEmits(['update:modelValue']);

// 回显的文件列表
const fileList = computed(() => {
  if (!props.modelValue) {
    return [];
  }
  // 字符串类型
  if (props.modelValue && typeof props.modelValue === 'string') {
    return [buildFileObject(props.modelValue)];
  }
  // 数组类型
  let fl = []
  if (props.modelValue && props.modelValue.length > 0) {
    for (let i = 0; i < props.modelValue.length; i++) {
      fl.push(buildFileObject(props.modelValue[i]));
    }
  }
  return fl;
});

// 组件挂载完成后执行
onMounted(() => {
  // 初始化上传组件
  http
    .get({
      url: `/thirdparty/tencent/cos/credential`,
    })
    .then(({ data }) => {
      if (http.success(data)) {
        let body = data.body
        cos.value = new COS({
          getAuthorization: (options, callback) =>
            callback({
              TmpSecretId: body.credentials.tmpSecretId,
              TmpSecretKey: body.credentials.tmpSecretKey,
              XCosSecurityToken: body.credentials.sessionToken,
              StartTime: body.startTime,
              ExpiredTime: body.expiredTime
            })
        })
        path.value = body.path;
        disabled.value = false;
      }
    });
});

// 生成文件对象
const buildFileObject = (url) => {
  let fileName = url.substr(url.lastIndexOf('/') + 1)
  let hash = hashCode(fileName)
  return {
    url: url,
    uid: hash,
    name: fileName,
    raw: {
      uid: hash
    }
  }
};

// 生成文件的 hash 值
const hashCode = (str) => {
  let hash = 0
  if (str.length === 0) return hash
  for (let i = 0; i < str.length; i++) {
    let char = str.charCodeAt(i)
    hash = ((hash << 5) - hash) + char
    hash = hash & hash
  }
  return hash
};

// 设置组件的值
const setValue = (fl) => {
  emit("update:modelValue", fl);
};

// 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用
const handleChangeEvent = (f, fl) => {
  // 上传前修改文件的标识
  let hash = hashCode(f.name);
  f.uid = hash;
  f.raw.uid = hash;
  file.value = f;
};

// 删除文件处理事件
const handleRemoveEvent = (f, fl) => {
  if (props.limit === 1) {
    setValue('');
    return;
  }
  let values = [];
  for (let i = 0; i < fl.length; i++) {
    if (fl[i] && fl[i].name === file.name) {
      continue;
    }
    values.push(fl[i].url);
  }
  setValue(values);
};

// 文件上传前处理
const handleBeforeUploadEvent = (f) => {
  // 文件名称不能重复
  for (let i = 0; i < fileList.value.length; i++) {
    if (fileList.value[i] && fileList.value[i].name === file.name) {
      ElMessage.error('文件名不能重复！')
      return false
    }
  }
  return true
};

// 文件上传处理
const handleUploadEvent = (p) => {
  cos.value.putObject({
    Bucket: config.bucket,
    Region: config.region,
    Key: path.value + file.value.name,
    StorageClass: config.storageClass,
    Body: file.value.raw
  }, (err, data) => {
    if (err) {
      ElMessage.error(`文件上传失败！错误原因：${err.message}`)
      return;
    }
    if (data.statusCode !== 200) {
      return;
    }
    // 上传文件路径
    let filePath = 'https://' + data.Location;
    // 单文件上传处理
    if (props.limit === 1) {
      setValue(filePath);
      return;
    }
    // 多文件处理
    let values = [];
    for (let i = 0; i < props.modelValue.length; i++) {
      values.push(props.modelValue[i]);
    }
    values.push(filePath);
    setValue(values);
    ElMessage.success('文件上传成功！');
  })
};

// 文件超出个数限制时的钩子
const handleExceedEvent = (file, fileList) => {
  ElMessage({
    message: `最多只能上传${props.limit}张图片`,
    type: 'warning',
    duration: 1000
  })
};
</script>
